/**
 * 
 * 选修课
 * 
 * 题目描述

现有两门选修课，每门选修课都有一部分学生选修，每个学生都有选修课的成绩，需要你找出同时选修了两门选修课的学生，先按照班级进行划分，班级编号小的先输出，每个班级按照两门选修课成绩和的降序排序，成绩相同时按照学生的学号升序排序。

输入描述
第一行为第一门选修课学生的成绩，

第二行为第二门选修课学生的成绩，

每行数据中学生之间以英文分号分隔，每个学生的学号和成绩以英文逗号分隔，

学生学号的格式为8位数字

2位院系编号+入学年份后2位+院系内部1位专业编号+所在班级3位学号
学生成绩的取值范围为[0,100]之间的整数，

两门选修课选修学生数的取值范围为[1-2000]之间的整数。

输出描述
同时选修了两门选修课的学生的学号，如果没有同时选修两门选修课的学生输出NULL，

否则，先按照班级划分，班级编号小的先输出，每个班级先输出班级编号(学号前五位)，

然后另起一行输出这个班级同时选修两门选修课的学生学号，学号按照要求排序(按照两门选修课成绩和的降序，成绩和相同时按照学号升序学生之间以英文分号分隔。

用例
输入	01202021,75;01201033,95;01202008,80;01203006,90;01203088,100
01202008,70;01203088,85;01202111,80;01202021,75;01201100,88
输出	01202
01202008;01202021
01203
01203088
说明	
同时选修了两选修课的学生01202021、01202008、01203088，这三个学生两门选修课的成绩和分别为150、150、185，
01202021、01202008属于01202班的学生，按照成绩和降序，成绩相同时按学号升序输出的结果为01202008;01202021,
01203088属于01203班的学生，按照成绩和降序，成绩相同时按学号升序输出的结果为01203088，
01202的班级编号小于01203的班级编号，需要先输出。
输入	01201022,75;01202033,95;01202018,80;01203006,90;01202066,100
01202008,70;01203102,85;01202111,80;01201021,75;01201100,88
输出	NULL
说明	没有同时选修了两门选修课的学生，输出NULL.
 */

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Scanner;

/**
  * 
  题目解析
本题主要考察自定义排序。

本题有两种解题思路：

思路一：统计出所有学生信息（学号，班级编号，选修课数量，总分）
排序逻辑 

首先按照选修课数量降序（保证选修两门的在前）
如果选修课数量相同，则继续按照班级编号升序（字典序升序，保证同班的学生在一起）
如果班级编号也相同，则继续按照总分降序
如果总分也相同，则继续按照学号升序（字典序升序）
排序后，遍历学生集合时，我们主要需要注意：

当前学生选修课数量不足两门时，停止打印
当前学生和上一个学生的班级编号不同时，则当前学生为新班级的，我们应该另起一行，打印新班级学生信息。
思路二：按照课程分类，分别统计出选修了课程一的学生集合，以及课程二的学生集合
首先，过滤出课程一和课程二都有的学生，

然后，将同时选修两门课程的学生，按照班级编号分类统计

之后，对每个班级编号下面的学生进行排序：

总分降序
总分相同，则学号（字典序）升序
最后，打印各个班级的编号，以及其下的学生学号。

下面代码实现中，C语言用的思路一（主要是C语言没有map），其他语言用了思路二。
  */
public class 选修课 {
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
 
        // key为学生学号, value为该学生选修课程一的成绩
        HashMap<String, Integer> course1 = new HashMap<>();
        parse(sc.nextLine(), course1);
 
        // key为学生学号, value为该学生选修课程二的成绩
        HashMap<String, Integer> course2 = new HashMap<>();
        parse(sc.nextLine(), course2);
 
        // key为班级编号, value为该班级同时选修两门课程的学生学号集合
        HashMap<String, ArrayList<String>> classes = new HashMap<>();
 
        // 找出同时选修了两门课程的学生学号
        for (String stuId : course1.keySet()) {
            if (course2.containsKey(stuId)) {
                // 班级编号(学号前五位)
                String classId = stuId.substring(0, 5);
 
                // 该班级同时选修两门课程的学生学号集合
                classes.putIfAbsent(classId, new ArrayList<>());
                classes.get(classId).add(stuId);
            }
        }
 
        // 如果没有同时选修两门选修课的学生输出NULL
        if (classes.isEmpty()) {
            System.out.println("NULL");
            return;
        }
 
        // 先按照班级划分，班级编号小的先输出（班级编号字典序升序）
        classes.keySet().stream().sorted(String::compareTo).forEach(classId -> {
            classes.get(classId).sort((a, b) -> {
                int scoreA = course1.get(a) + course2.get(a);
                int scoreB = course1.get(b) + course2.get(b);
 
                if (scoreA != scoreB) {
                    // 按照两门选修课成绩和的降序
                    return scoreB - scoreA;
                } else {
                    // 成绩和相同时按照学号升序
                    return a.compareTo(b);
                }
            });
 
            // 先打印班级编号
            System.out.println(classId);
            // 再打印该班级中同时选修两门课程的学生学号
            System.out.println(String.join(";", classes.get(classId)));
        });
    }
 
    public static void parse(String s, HashMap<String, Integer> course) {
        String[] students = s.split(";");
 
        for (String student : students) {
            String[] info = student.split(",");
            String id = info[0];
            int score = Integer.parseInt(info[1]);
            course.put(id, score);
        }
    }
}
